home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / AdobeExamples / NX_LineDraw / DrawView.m < prev    next >
Text File  |  1995-06-12  |  13KB  |  491 lines

  1.  
  2. /*
  3.  * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
  4.  *
  5.  * (b)  If this Sample Code is distributed as part of the Display PostScript
  6.  *    System Software Development Kit from Adobe Systems Incorporated,
  7.  *    then this copy is designated as Development Software and its use is
  8.  *    subject to the terms of the License Agreement attached to such Kit.
  9.  *
  10.  * (c)  If this Sample Code is distributed independently, then the following
  11.  *    terms apply:
  12.  *
  13.  * (d)  This file may be freely copied and redistributed as long as:
  14.  *    1) Parts (a), (d), (e) and (f) continue to be included in the file,
  15.  *    2) If the file has been modified in any way, a notice of such
  16.  *      modification is conspicuously indicated.
  17.  *
  18.  * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
  19.  *    Adobe Systems Incorporated.
  20.  * 
  21.  * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
  22.  *    CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
  23.  *    AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
  24.  *    ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
  25.  *    OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
  26.  *    WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
  27.  *    WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
  28.  *    DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
  29.  *    FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
  30.  *    OF THIRD PARTY RIGHTS.
  31.  */
  32.  
  33. /*
  34.  *    DrawView.m
  35.  *
  36.  *    The purpose of the application is to show different methods for drawing paths and 
  37.  *    the times obtained for each method.  Lines are used in the construction of the paths
  38.  *    but curves and arcs could be used as well.
  39.  *
  40.  *    This file contains the methods for the DrawView class, a subclass of view.
  41.  *    The important methods to note are the methods to draw the lines.
  42.  *
  43.  *    Version:    2.0
  44.  *    Author:    Ken Anderson, Ken Fromm
  45.  *    History:
  46.  *            03-07-91        Added this comment.
  47.  */
  48.  
  49. #import "DrawView.h"
  50. #import "DrawViewWraps.h"
  51. #import <appkit/Button.h>
  52. #import <appkit/Control.h>
  53. #import <appkit/Matrix.h>
  54. #import <appkit/TextField.h>
  55. #import <dpsclient/wraps.h>
  56.  
  57. @implementation DrawView
  58.  
  59. /*
  60. * A gstate is allocated for this view and the clippling is set to NO.  The gstate is used
  61. * because the view is considered important enough to have one.  The clipping is set to NO
  62. * because it can be expensive.  In addition, the lines are constructed to fall entirely within the view.
  63. * The initialization is done here because the "initailize" method is too late for instance
  64. * variables that are involved in the display of the view.
  65. */
  66. -initFrame:(const NXRect *) frameRect
  67. {
  68.     [super  initFrame:frameRect]; 
  69.  
  70.     [self allocateGState]; 
  71.     [self setClipping:NO];
  72.     
  73.     srand(1);
  74.     TotalLines = 0;
  75.         
  76.     PSTrace = Random = NO;
  77.  
  78.     PSWDefs();
  79.     
  80.     return self;
  81. }
  82.  
  83. /* Since we allocate a gstate, why not free it.  In all likelihood, the gstate is freed anyways.
  84. * A freeGState method is available, however, so conservative programming dictates that it be used.
  85. */ 
  86. - free
  87. {
  88.     [self  freeGState];
  89.     
  90.     return [super free];
  91. }
  92.  
  93. /*
  94. *  This method is used to obtain the ids of the text fields to display the times.
  95. */
  96. -setMatrixDisplayTimes:anObject;
  97. {
  98.     matrixDisplayTimes = anObject;
  99.     return self;
  100. }
  101.  
  102. /*
  103. *  These two methods are used to obtain the ids of the line color and line width sliders.
  104. */
  105.  
  106. -setSliderColor:anObject
  107. {
  108.     sliderColor = anObject;
  109.     LineColor = [sliderColor floatValue];
  110.  
  111.     return self;
  112. }
  113.  
  114. -setSliderWidth:anObject
  115. {
  116.     sliderWidth = anObject;
  117.     LineWidth = [sliderWidth floatValue];
  118.  
  119.     return self;
  120. }
  121.  
  122. /* Id of total lines text field. */
  123. -setFieldTotalLines:anObject
  124. {
  125.     fieldTotalLines = anObject;
  126.  
  127.     return self;
  128. }
  129.  
  130. /* Messaged from the button to select the line color and width.  The sender is a matrix
  131. *  of buttons (a one cell matrix).  The state of the button is used to key the enabling or
  132. * disabling of the slider controls. At the end, the value of Random is flipped to make it
  133. * sematically correct. Otherwise, YES would be no and NO would be yes.
  134. */
  135. -selectColorWidth:sender
  136. {
  137.     Random = [[sender selectedCell] state];
  138.     [sliderColor setEnabled:Random];
  139.     [sliderWidth setEnabled:Random];
  140.     Random = !Random;
  141.     
  142.     return self;
  143. }
  144.  
  145. /* The sliders message these methods.  */
  146. -changeLineColor:sender
  147. {
  148.     LineColor = [sender floatValue];
  149.     
  150.     return self;
  151. }
  152.  
  153. -changeLineWidth:sender
  154. {
  155.     LineWidth = [sender floatValue];
  156.     
  157.     return self;
  158. }
  159.  
  160. /* This method changes the title of the menu cell according to the
  161. * value of the PSTrace variable.
  162. */
  163. -psTrace:sender
  164. {
  165.     if (PSTrace == NO)
  166.         [[sender selectedCell] setTitle:"Trace On"];
  167.     else
  168.         [[sender selectedCell] setTitle:"Trace Off"];
  169.     PSTrace = !PSTrace;
  170.     
  171.     return self;
  172. }
  173.  
  174. /* Erases the times. Messaged when new lines are made or the lines are cleared. */
  175. -eraseTimes:sender
  176. {
  177.     int        i;
  178.     
  179.     for (i = 0; i < [matrixDisplayTimes cellCount]; i++)
  180.         [[matrixDisplayTimes cellAt:i :0] setStringValue:""];
  181.  
  182.     return self;
  183. }
  184.  
  185. /* This method is messaged by the row of buttons to make lines.  The sender is a matrix
  186. * so the tag of the selected cell is used to obtain the number of lines to make.  If the total
  187. * number of lines exceeds the size of the line arrays, then system beep; otherwise, 
  188. * randomly create the lines and populate the arrays.  If Random is set, the line color and
  189. * width are randomly selected with each line.  This is the worst case displaying lines,
  190. * especially with the OptimizedStroke.  If Random is not set then the current values
  191. * of LineColor and LineWidth are used.
  192. */
  193. -makeLines:sender
  194. {
  195.     int  Numsetlines, j;
  196.  
  197.     [self  eraseTimes:self];
  198.     
  199.     Numsetlines = [sender selectedTag];
  200.  
  201.     if (TotalLines >= MAXARRAY)
  202.         NXBeep();
  203.     else
  204.     {
  205.         if (TotalLines + Numsetlines > MAXARRAY)
  206.             Numsetlines = MAXARRAY - TotalLines;
  207.  
  208.         for (j = TotalLines; j < TotalLines + Numsetlines; j++)
  209.         {
  210.             X[j] = rand () % ((int)bounds.size.width -4) +2;
  211.             Y[j] = rand () % ((int)bounds.size.height -4) +2;
  212.             X1[j] = rand () % ((int)bounds.size.width -4) +2;
  213.             Y1[j] = rand () % ((int)bounds.size.height -4) +2;
  214.             if (Random)
  215.             {
  216.                 C[j] = (rand() % 1000) * 0.001;
  217.                 W[j] = (rand() % (MAXWIDTH * 10)) * 0.1;
  218.             }
  219.             else
  220.             {
  221.                 C[j] = LineColor;
  222.                 W[j] = LineWidth;
  223.             }
  224.         }
  225.         
  226.         TotalLines += Numsetlines;
  227.         [fieldTotalLines setIntValue:TotalLines];
  228.     }
  229.  
  230.     return self;
  231. }
  232.  
  233. -clearLines:sender
  234. {
  235.     TotalLines = 0;
  236.     [fieldTotalLines setIntValue:TotalLines];
  237.  
  238.     [self  eraseTimes:self];
  239.     
  240.     return self;
  241. }
  242.  
  243. /*
  244. *   Messaged by the method drawing matrix of buttons.
  245. *   Messages display which in turn will message drawSelf::. 
  246. */
  247. -drawViewOne:sender
  248. {
  249.     int    i, row;
  250.     
  251.     row = [sender selectedRow];
  252.         drawFlags.field = 0x80 >> row;
  253.     
  254.     [self display];
  255.  
  256.     return self;
  257. }
  258.  
  259. /*  Messaged by "Draw All" button. Messages display which in turn will message drawSelf::.  */
  260. -drawViewAll:sender
  261. {
  262.     drawFlags.field = DRAWALL;
  263.  
  264.     [self display];
  265.  
  266.     return self;
  267. }
  268.  
  269. /*
  270. * Below are five methods.  Each uses a different approach to drawing lines.
  271. * In some cases, the approaches are similar. Each is annotated.
  272. */
  273.  
  274. /* This approach uses the single operator function calls which are quite easy to use because
  275. * no wraps are necessary.  The drawback is that each call is a separate message to
  276. * the interpreter whereas a wrap is just one message.  This approach is fine for simple
  277. * drawing but wraps should be used for anything over a few lines and repetitions.
  278. */
  279. -drawSingleOps:(int) cell
  280. {
  281.     int        ElapsedTime,counter;
  282.     
  283.     [[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
  284.  
  285.     PSWMarkTime(); NXPing ();    
  286.     if (PSTrace)
  287.         DPSTraceContext(DPSGetCurrentContext(), YES);
  288.  
  289.     PSsetgray (BGCOLOR);
  290.     PSrectfill (0.0, 0.0, bounds.size.width, bounds.size.height);
  291.     PSsetgray (BGSTRCOLOR);
  292.     PSsetlinewidth (BGSTRWIDTH);
  293.     PSrectstroke (0.0, 0.0, bounds.size.width, bounds.size.height);
  294.  
  295.     for (counter = 0; counter < TotalLines; counter++)
  296.     {
  297.         PSsetlinewidth(W[counter]);
  298.         PSsetgray (C[counter]);
  299.         PSmoveto (X[counter], Y[counter]);    
  300.         PSlineto (X1[counter], Y1[counter]);
  301.         PSstroke ();
  302.     }    
  303.  
  304.     if (PSTrace)
  305.         DPSTraceContext(DPSGetCurrentContext(), NO);
  306.     PSWReturnTime (&ElapsedTime);
  307.  
  308.     [[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
  309.  
  310.     return self;
  311. }
  312.  
  313. /* This approach creates wraps to erase the view and draw the lines.  The appropriate
  314. * arguments are passed to each wrap.  Compare this method with the one that follows.
  315. * The time difference between the two is slight.  As in the singler operator approach,
  316. * this method is fine for drawings that are displayed infrequently.  For drawings that
  317. * are repeated, binding is suggested.  See the comments in DrawViewWraps.psw
  318. * for more information on binding.
  319. */
  320. -drawWraps:(int) cell
  321. {
  322.     int        ElapsedTime, counter;
  323.     float        ViewRect[4];
  324.     
  325.     [[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
  326.  
  327.     PSWMarkTime (); NXPing ();
  328.     if (PSTrace)
  329.         DPSTraceContext(DPSGetCurrentContext(), YES);
  330.  
  331.     ViewRect[0] = ViewRect[1] = 0.0;
  332.     ViewRect[2] = bounds.size.width;
  333.     ViewRect[3] = bounds.size.height;
  334.     PSWEraseView (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
  335.         
  336.     for (counter = 0; counter < TotalLines; counter++)
  337.     {
  338.         PSWDrawLine (W[counter], C[counter], X[counter], Y[counter],
  339.             X1[counter], Y1[counter]);
  340.     }
  341.  
  342.     if (PSTrace)
  343.         DPSTraceContext(DPSGetCurrentContext(), NO);
  344.     PSWReturnTime (&ElapsedTime);
  345.  
  346.     [[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
  347.  
  348.     return self;
  349. }
  350.  
  351. /* The wrap calls used in this approach make use of definitions contained in PSWDefs().
  352. *  The definitions bind their operators which simply means that the name lookup occurs
  353. * at definition time only once rather than with each interpretation of the definition (which
  354. * can occur many times).  This approach is recommended for more than the simplest
  355. * drawing.  Drawings with lines that are of uniform line color and width might benefit
  356. * from the opitimized stroke method.  Random line colors and widths show this to be
  357. * the fastest. 
  358. */
  359. -drawWrapsBind:(int) cell
  360. {
  361.     int        ElapsedTime, counter;
  362.     float        ViewRect[4];
  363.     
  364.     [[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
  365.  
  366.     PSWMarkTime (); NXPing ();
  367.     if (PSTrace)
  368.         DPSTraceContext(DPSGetCurrentContext(), YES);
  369.  
  370.     ViewRect[0] = ViewRect[1] = 0.0;
  371.     ViewRect[2] = bounds.size.width;
  372.     ViewRect[3] = bounds.size.height;
  373.     PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
  374.         
  375.     for (counter = 0; counter < TotalLines; counter++)
  376.     {
  377.         PSWDrawLineBind (W[counter], C[counter], X[counter], Y[counter],
  378.             X1[counter], Y1[counter]);
  379.     }
  380.  
  381.     if (PSTrace)
  382.         DPSTraceContext(DPSGetCurrentContext(), NO);
  383.     PSWReturnTime (&ElapsedTime);
  384.  
  385.     [[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
  386.  
  387.     return self;
  388. }    
  389.  
  390.  
  391. /*  This approach is uses the interpreter to loop through the array and draw the
  392. * lines.  As a result, a good deal of stack manipulation must be performed in the wrap.
  393. * The timing results show this to be a poor approach.  Not recommended at this time.
  394. */
  395. -drawWrapsRepeat:(int) cell
  396. {
  397.     int        ElapsedTime;
  398.     float        ViewRect[4];
  399.     
  400.     [[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
  401.  
  402.     PSWMarkTime (); NXPing ();
  403.     if (PSTrace)
  404.         DPSTraceContext(DPSGetCurrentContext(), YES);
  405.  
  406.     ViewRect[0] = ViewRect[1] = 0.0;
  407.     ViewRect[2] = bounds.size.width;
  408.     ViewRect[3] = bounds.size.height;
  409.     PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
  410.     PSWDrawLineRepeatBind (W, C, X, Y, X1, Y1, TotalLines);
  411.  
  412.     if (PSTrace)
  413.         DPSTraceContext(DPSGetCurrentContext(), NO);
  414.     PSWReturnTime (&ElapsedTime);
  415.  
  416.     [[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
  417.  
  418.     return self;
  419. }
  420.  
  421. /* This approach builds the path with successive moveto's and lineto's until the end of the
  422. * array or a change in line color or width occurs.  At this point, the path is stroked.  The
  423. * timing studies show this to be an efficient approach when the line color and width appear
  424. * in non-random order.  As a result, this approach might be used to display a grid or other
  425. * similar uniform drawing.  NOTE: as of this writing the path stack contains a 1500 point limit.
  426. * Therefore, a check should be included to stroke if the limit has been reached.  Otherwise
  427. * a limiterror may occur.
  428. */
  429. -drawOptimizedStroke:(int) cell
  430. {
  431.     int        ElapsedTime,counter;
  432.     float        ViewRect[4];
  433.     
  434.     [[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
  435.  
  436.     PSWMarkTime (); NXPing ();
  437.     if (PSTrace)
  438.         DPSTraceContext(DPSGetCurrentContext(), YES);
  439.  
  440.     ViewRect[0] = ViewRect[1] = 0.0;
  441.     ViewRect[2] = bounds.size.width;
  442.     ViewRect[3] = bounds.size.height;
  443.     PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
  444.         
  445.     for (counter = 0; counter < TotalLines; counter++)
  446.     {
  447.         PSWMakeLineBind (X[counter], Y[counter], X1[counter], Y1[counter]);
  448.  
  449.         if (counter >= TotalLines -1 ||
  450.             C[counter] != C[counter + 1] ||
  451.                 W[counter] != W[counter+1])
  452.         {
  453.             PSWStrokeLineBind (W[counter], C[counter]);
  454.         }
  455.     }
  456.  
  457.     if (PSTrace)
  458.         DPSTraceContext(DPSGetCurrentContext(), NO);
  459.     PSWReturnTime (&ElapsedTime);
  460.  
  461.     [[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
  462.  
  463.     return self;
  464. }
  465.  
  466. /* Messaged by the "display" method.  This method should not be called directly. */
  467. -drawSelf:(NXRect *)r :(int) count
  468. {    
  469.     float    ViewRect[4];
  470.     
  471.     ViewRect[0] = ViewRect[1] = 0.0;
  472.     ViewRect[2] = bounds.size.width;
  473.     ViewRect[3] = bounds.size.height;
  474.     PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
  475.  
  476.     if (drawFlags.flags.singleops)
  477.         [self drawSingleOps:0];
  478.     if (drawFlags.flags.wraps)
  479.         [self drawWraps:1];
  480.     if (drawFlags.flags.bind)
  481.         [self drawWrapsBind:2];
  482.     if (drawFlags.flags.repeat)
  483.         [self drawWrapsRepeat:3];
  484.     if (drawFlags.flags.optimized)
  485.         [self drawOptimizedStroke:4];
  486.  
  487.     return self;
  488. }
  489.  
  490. @end
  491.